__author__ = 'JackSong'

from abc import ABC

import redis
import tornado.web
import tornado.websocket
from daemon import Bridge
from utils.data import ClientData
from utils.tools import check_ip,check_port
from tornado.gen import coroutine,Return
from utils.tools import PrpcryptInitData
from utils.multithreading import executor
from tornado.stack_context import wrap
from model.host import HostComputer


class BaseHandler(tornado.web.RequestHandler,ABC):

    def initialize(self,middleware=None):
        self.session = self.application.session()
        self.access_log = self.application.access_log
        self.redis = redis.StrictRedis(connection_pool = self.application.redis)
        self.middlewares = middleware

    def get_id(self):
        return id(self)

    def on_finish(self):
        self.session.close()
        del self.redis

    def check_host_belongs_to_user(self):
        pass

    def prepare(self):
        if self.middlewares:
            for middleware in self.middlewares:
                status,msg = middleware.process_request(self)
                if not status:
                    self.write({'status':False,"response":msg})
                    self.finish()
                    return


class IndexHandler(BaseHandler,ABC):
    def get(self):
        self.render("dist/index.html")


class WSHandler(tornado.websocket.WebSocketHandler,ABC):
    clients = dict()
    COMMAND = str()

    def initialize(self):
        self.session = self.application.session()
        self.queues = self.application.queues
        self.access_log = self.application.access_log

    def on_connection_close(self):
        self.session.close()

    def get_client(self):
        return self.clients.get(self._id(),None)

    def put_client(self):
        bridge = Bridge(self)
        self.clients[self._id()] = bridge

    def remove_client(self):
        bridge = self.get_client()
        if bridge:
            bridge.destroy()
            client = self.clients.pop(self._id(),None)
            if client: del client

    @staticmethod
    def _check_init_param(data):
        return check_ip(data.ip) and check_port(data.port)

    @coroutine
    def _get_host(self,data):
        data = PrpcryptInitData(**data)
        query = self.session.query(HostComputer).filter_by(id = data.id)
        host = yield executor(wrap(query.first))
        raise Return(host)

    @staticmethod
    def _is_init_data(data):
        return data.get_type() == 'init'

    def _id(self):
        return id(self)

    def open(self):
        self.put_client()

    @coroutine
    def on_message(self,message):
        bridge = self.get_client()
        client_data = ClientData(message)
        if self._is_init_data(client_data):
            host = yield self._get_host(client_data.data)
            if self._check_init_param(host):
                yield executor(wrap(bridge.open),*(host,))
                self.access_log.info('connection established from: %s' % self._id())
            else:
                self.remove_client()
                self.access_log.info('init param invalid: %s' % client_data.data)
        else:
            if bridge.status:
                command = client_data.data
                rest = yield executor(wrap(bridge.trans_forward),*(command,))
                if not rest:
                    bridge.destroy()
                    self.close()

    def on_close(self):
        bridge = self.get_client()
        bridge.destroy()
        self.remove_client()
        self.access_log.info('client close the connection: %s' % self._id())

    def check_origin(self,origin):
        return True
